#include "triangulation.h"

namespace Packing
{
	Polygon_triangulation::Polygon_triangulation(const std::vector<Point>& boundary_vertices, unsigned int nb_sample)
	{
		CGAL_polygon bd_polygon(boundary_vertices.begin(), boundary_vertices.end());
		double perimeter = 0.0;
		std::vector<double> edge_len(bd_polygon.size());
		for (unsigned int i = 0; i < bd_polygon.size(); i++)
		{
			edge_len[i] = CGAL::sqrt(bd_polygon.edge(i).squared_length());
			perimeter += edge_len[i];
		}
		for (unsigned int i = 0; i < bd_polygon.size(); i++)
		{
			unsigned int n = std::max<unsigned int>(edge_len[i]/perimeter*nb_sample, 1);
			CGAL_polygon::Segment_2 e = bd_polygon.edge(i);
			for (unsigned int j = 0; j < n; j++)
			{
				Point p = e.source() + j*e.to_vector()/n;
				boundary_pnts.push_back(p);
			}
		}
	}
	void Polygon_triangulation::clear()
	{
		std::for_each(vgroups.begin(), vgroups.end(), std::mem_fun_ref(&Vert_group::clear));
		vgroups.clear();
		dt.clear();
	}


	void Polygon_triangulation::insert_polygons(const std::vector<Packing_polygon*>& polygon_set, unsigned int sample_nb)
	{
		unsigned int idx;
		try
		{
			clear();
			std::vector<Vertex_handle> bdvh;
			bdvh.reserve(boundary_pnts.size()+1);
			for (unsigned int i = 0; i < boundary_pnts.size(); i++)
			{
				Vertex_handle vh = dt.insert(boundary_pnts[i]);
				vh->group_id = -1;
				bdvh.push_back(vh);
			}

			for (unsigned int i = 0; i < bdvh.size(); i++)
				dt.insert_constraint(bdvh[i], bdvh[(i+1)%bdvh.size()]);

			vgroups.resize(polygon_set.size());
			for (unsigned int i = 0; i < polygon_set.size(); i++)
			{
				double perimeter = 0.0;
				std::vector<double> edge_len(polygon_set[i]->size());
				for (unsigned int j = 0; j < polygon_set[i]->size(); j++)
				{
					edge_len[j] = CGAL::sqrt(polygon_set[i]->edge(j).squared_length());
					perimeter += edge_len[j];
				}
				for (unsigned int j = 0; j < polygon_set[i]->size(); j++)
				{
					unsigned int n = std::max<unsigned int>(edge_len[j] / perimeter * sample_nb, 1);
					Packing_polygon::Segment_2 e = polygon_set[i]->edge(j);
					for (unsigned int k = 0; k < n; k++)
					{
						Point p = e.source() + k*e.to_vector()/n;
						Vertex_handle vh = dt.insert(p);
						vh->group_id = i;
						vgroups[i].push_back(vh);
					}
				}
				// specify constraints
				for (unsigned int j = 0; j < vgroups[i].size(); j++)
				{
					idx = i;
					dt.insert_constraint(vgroups[i][j], vgroups[i][(j+1)%vgroups[i].size()]);
				}
			}
		}
		catch (std::exception& )
		{
			Intersect_constraints e;
			e.index(idx);
			throw e;
		}
		construct_voronoi_cells();
		construct_cat_cells();
		// update face normal
		for (Face_iterator fit = dt.finite_faces_begin(); fit != dt.finite_faces_end(); ++fit)
		{
			Vertex_handle v0 = fit->vertex(0), v1 = fit->vertex(1), v2 = fit->vertex(2);
			Vector_2 v01(v0->point(), v1->point()), v02(v0->point(), v2->point());
			fit->normal_orientation = CGAL::sign(v01.x()*v02.y() - v01.y()*v02.x());
		}
	}

	void Polygon_triangulation::transform_vertex_at_group(unsigned int id, const Parameter& p)
	{
		Parameter::Transformation_2 t = p.to_transformation();
		for (unsigned int i = 0; i < vgroups[id].size(); i++)
			vgroups[id][i]->set_point(t(vgroups[id][i]->point()));
	}

	void Polygon_triangulation::gl_draw_triangulation(const GLfloat* color) const
	{
		glColor3fv(color);
		glLineWidth(1.2f);
		glBegin(GL_LINES);
		for (Edge_iterator eit = dt.finite_edges_begin(); eit != dt.finite_edges_end(); ++eit)
		{
			Vertex_handle v0 = eit->first->vertex(eit->first->cw(eit->second)), v1 = eit->first->vertex(eit->first->ccw(eit->second));
			if (v0->group_id != v1->group_id)
			{
				glVertex2d(v0->point().x(), v0->point().y());
				glVertex2d(v1->point().x(), v1->point().y());
			}
		}
		glEnd();
	}

	void Polygon_triangulation::gl_draw_intergroup_voronoi(const GLfloat *color/*, unsigned int group_id*/) const
	{
		glColor3fv(color);
		glLineWidth(1.5f);
		for (Vertex_iterator vit = dt.vertices_begin(); vit != dt.vertices_end(); ++vit)
		{
			if (vit->group_id < 0 || dt.is_infinite(vit) || vit->voronoi_vtx.size() <= 1)
				continue;
			glBegin(GL_LINE_STRIP);
			//if (vit->group_id == group_id)
				for (std::vector<Point>::const_iterator i = vit->voronoi_vtx.begin(); i != vit->voronoi_vtx.end(); ++i)
					glVertex2d(i->x(), i->y());
			glEnd();
		}
	}
	void Polygon_triangulation::gl_draw_intergroup_cat(const GLfloat *color/*, unsigned int group_id*/) const
	{
		glColor3fv(color);
		glLineWidth(1.5f);
		for (Vertex_iterator vit = dt.vertices_begin(); vit != dt.vertices_end(); ++vit)
		{
			if (vit->group_id < 0 || dt.is_infinite(vit) || vit->cat_vtx.size() <= 1)
				continue;
			glBegin(GL_LINE_STRIP);
			//if (vit->group_id == group_id)
			for (std::vector<Point>::const_iterator i = vit->cat_vtx.begin(); i != vit->cat_vtx.end(); ++i)
				glVertex2d(i->x(), i->y());
			glEnd();
		}
	}
	void Polygon_triangulation::construct_voronoi_cells()
	{
		for (Constraint_Delaunay_triangulation_2::Finite_vertices_iterator vit = dt.finite_vertices_begin(); vit != dt.finite_vertices_end(); ++vit)
		{
			if (vit->group_id < 0)
				continue;

			Face_circulator start_face = dt.incident_faces(vit);
			Face_circulator end_face = start_face;
			Vertex_handle v0 = start_face->vertex(0), v1 = start_face->vertex(1), v2 = start_face->vertex(2);
			while ( is_intergroup_face(start_face) )
			{
				++start_face;
				if (start_face == end_face)	break;
			}
			if ( is_intergroup_face(start_face) )
				std::cout<<"Singular vertex!\n";
			Face_circulator fc = start_face;
			do
			{
				int idx;
				bool valid = fc->has_vertex(vit, idx);
				assert( valid );
				Vertex_handle vcw = fc->vertex(fc->cw(idx)), vccw = fc->vertex(fc->ccw(idx));

				if (is_intergroup_face(fc))
				{
					if (vcw->group_id < 0 && vccw->group_id >= 0 && vit->group_id != vccw->group_id)
					{
						vit->voronoi_vtx.push_back(CGAL::circumcenter(vit->point(), vcw->point(), vccw->point()));
					}
					else if (vcw->group_id < 0 && vccw->group_id >= 0 && vit->group_id == vccw->group_id)
					{
						vit->voronoi_vtx.push_back(vcw->point());
					}
					else if (vcw->group_id >= 0 && vccw->group_id < 0 && vit->group_id != vcw->group_id)
					{
						vit->voronoi_vtx.push_back(CGAL::circumcenter(vit->point(), vcw->point(), vccw->point()));
						//vit->voronoi_vtx.push_back(CGAL::midpoint(vcw->point(), vccw->point()));
						//vit->voronoi_vtx.push_back(CGAL::circumcenter(vit->point(), vcw->point(), vccw->point()));
					}
					else if (vcw->group_id >= 0 && vccw->group_id < 0 && vit->group_id == vcw->group_id)
					{
						vit->voronoi_vtx.push_back(vccw->point());
						//vit->voronoi_vtx.push_back(CGAL::midpoint(vcw->point(), vccw->point()));
						//vit->voronoi_vtx.push_back(CGAL::circumcenter(vit->point(), vcw->point(), vccw->point()));
					}
					else if (vcw->group_id < 0 && vccw->group_id < 0)
					{
						//vit->voronoi_vtx.push_back(vcw->point());
						vit->voronoi_vtx.push_back(vccw->point());
					}
					else
					{
						Point c = CGAL::circumcenter(vit->point(), vcw->point(), vccw->point());
						vit->voronoi_vtx.push_back(c);
					}
				}
				++fc;
			} while (fc != start_face);
			std::vector<Polygon_triangulation::Point>::iterator it = std::unique(vit->voronoi_vtx.begin(), vit->voronoi_vtx.end());
			vit->voronoi_vtx.resize(it-vit->voronoi_vtx.begin());
		}
	}
	void Polygon_triangulation::construct_cat_cells()
	{
		for (Constraint_Delaunay_triangulation_2::Finite_vertices_iterator vit = dt.finite_vertices_begin(); vit != dt.finite_vertices_end(); ++vit)
		{
			if (vit->group_id < 0)
				continue;

			Face_circulator start_face = dt.incident_faces(vit);
			Face_circulator end_face = start_face;
			Vertex_handle v0 = start_face->vertex(0), v1 = start_face->vertex(1), v2 = start_face->vertex(2);
			while ( is_intergroup_face(start_face) )
			{
				++start_face;
				if (start_face == end_face)	break;
				//v0 = start_face->vertex(0);
				//v1 = start_face->vertex(1);
				//v2 = start_face->vertex(2);
			}
			if ( is_intergroup_face(start_face) )
				std::cout<<"Singular vertex!\n";
			Face_circulator fc = start_face;
			do
			{
				int idx;
				bool valid = fc->has_vertex(vit, idx);
				assert( valid );
				Vertex_handle vcw = fc->vertex(fc->cw(idx)), vccw = fc->vertex(fc->ccw(idx));
				if (is_intergroup_face(fc))
				{
					if (vit->group_id == vcw->group_id && vit->group_id != vccw->group_id && vccw->group_id >= 0)
					{
						vit->cat_vtx.push_back(CGAL::midpoint(vit->point(), vccw->point()));
						vit->cat_vtx.push_back(CGAL::midpoint(vcw->point(), vccw->point()));
					}
					else if (vit->group_id == vcw->group_id && vit->group_id != vccw->group_id && vccw->group_id < 0)
						vit->cat_vtx.push_back(vccw->point()); // boundary point
					else if (vit->group_id != vcw->group_id && vit->group_id == vccw->group_id && vcw->group_id >= 0)
						vit->cat_vtx.push_back(CGAL::midpoint(vcw->point(), vccw->point()));
					else if (vit->group_id != vcw->group_id && vit->group_id == vccw->group_id && vcw->group_id < 0)
						vit->cat_vtx.push_back(vcw->point()); // boundary point
					else if (vit->group_id != vcw->group_id && vit->group_id != vccw->group_id && vcw->group_id == vccw->group_id && vcw->group_id >= 0)
						vit->cat_vtx.push_back(CGAL::midpoint(vit->point(), vccw->point()));
					else if (vit->group_id != vcw->group_id && vit->group_id != vccw->group_id && vcw->group_id == vccw->group_id && vcw->group_id < 0)
						vit->cat_vtx.push_back(vccw->point()); // boundary points
					else if (vit->group_id != vcw->group_id && vit->group_id != vccw->group_id && vcw->group_id != vccw->group_id && vcw->group_id >= 0 && vccw->group_id >= 0)
					{
						vit->cat_vtx.push_back(CGAL::midpoint(vit->point(), vccw->point()));
						vit->cat_vtx.push_back(CGAL::centroid(vit->point(), vcw->point(), vccw->point()));
					}
					else if (vit->group_id != vcw->group_id && vit->group_id != vccw->group_id && vcw->group_id != vccw->group_id && vcw->group_id < 0 && vccw->group_id >= 0)
					{// boundary point
						vit->cat_vtx.push_back(CGAL::midpoint(vit->point(), vccw->point()));
						vit->cat_vtx.push_back(vcw->point());
					}
					else if (vit->group_id != vcw->group_id && vit->group_id != vccw->group_id && vcw->group_id != vccw->group_id && vcw->group_id >=0 && vccw->group_id < 0)
					{// boundary point
						vit->cat_vtx.push_back(vccw->point());
						vit->cat_vtx.push_back(CGAL::midpoint(vit->point(), vcw->point()));
					}
				}
				++fc;
			} while (fc != start_face);
			std::vector<Polygon_triangulation::Point>::iterator it = std::unique(vit->cat_vtx.begin(), vit->cat_vtx.end());
			vit->cat_vtx.resize(it-vit->cat_vtx.begin());
		}
	}
}